package pdf.handler;

import java.awt.HeadlessException;
import java.awt.print.Book;
import java.awt.print.PageFormat;
import java.awt.print.Pageable;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Locale;

import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.IntegerSyntax;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.TextSyntax;
import javax.print.attribute.standard.Copies;
import javax.print.attribute.standard.JobName;
import javax.print.attribute.standard.RequestingUserName;
import javax.swing.JFileChooser;

/**
 * <p>
 * This class extends awt's PrinterJob, to provide a simple method of writing
 * PDF documents.
 * </p>
 * <p>
 * You can use this with any code that uses Java's printing mechanism. It does
 * include a few extra methods to provide access to some of PDF's features like
 * annotations, or outlines.
 * </p>
 * 
 * @author Gilbert DeLeeuw, gil1@users.sourceforge.net
 */
public class PDFPrinterJob extends PrinterJob
{

   /**
    * The file chooser
    */
   private static JFileChooser fileChooser;

   public static PrinterJob getPrinterJob()
   {
      return new PDFPrinterJob();
   }

   /**
    * Printing options;
    */
   private PrintRequestAttributeSet attributes;

   /**
    * PDF document properties
    */
   private PDFInfo info;

   /**
    * A pageable, or null
    */
   private Pageable pageable = null;

   /**
    * Page format.
    */
   private PageFormat pageFormat;

   /**
    * The Printable object to print.
    */
   private Printable printable;

   /**
    * The actual print job.
    */
   private PDFJob printJob;

   /**
    * Initializes a new instance of <code>PDFPrinterJob</code>.
    */
   public PDFPrinterJob()
   {
      attributes = new HashPrintRequestAttributeSet();
      info = new PDFInfo();
      pageFormat = new PageFormat(); // default page format.
      setJobName("Java Printing");
   }

   @Override
   public void cancel()
   {
      // Cancel is not an option
   }

   @Override
   public PageFormat defaultPage(PageFormat page)
   {
      return validatePage(page);
   }

   @Override
   public int getCopies()
   {
      return ((IntegerSyntax) attributes.get(Copies.class)).getValue();
   }

   @Override
   public String getJobName()
   {
      return ((TextSyntax) attributes.get(JobName.class)).getValue();
   }

   @Override
   public String getUserName()
   {
      return ((TextSyntax) attributes.get(RequestingUserName.class))
            .getValue();
   }

   @Override
   public boolean isCancelled()
   {
      return false;
   }

   @Override
   public PageFormat pageDialog(PageFormat page) throws HeadlessException
   {
      // No page dialog is supported.
      return (PageFormat) page.clone();
   }

   @Override
   public void print() throws PrinterException
   {
      File file;
      File path;
      String jobName = getJobName();

      if (fileChooser == null)
      {
         fileChooser = new JFileChooser();
         fileChooser.setMultiSelectionEnabled(false);
         fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
      }

      // Make sure job name is not blank
      if (jobName.equals(""))
         jobName = "Java Printing";
      // Eliminate invalid characters from job name.
      jobName = jobName.replaceAll("\\\\", "-");
      jobName = jobName.replaceAll("/", "-");

      path = fileChooser.getCurrentDirectory();
      file = new File(path, jobName + ".pdf");

      // Dialog to get file name...
      fileChooser.setSelectedFile(file);

      // Print
      if (fileChooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION)
      {
         file = fileChooser.getSelectedFile();
         print(file.getAbsolutePath());
      }
   }

   /**
    * Prints a set of pages.
    * 
    * @param pathname
    *            the full path for the output PDF file.
    * @exception PrinterException
    *                an error in the print system caused the job to be aborted.
    * @see Book
    * @see Pageable
    * @see Printable
    */
   public void print(String pathname) throws PrinterException
   {
      int pageCount;
      File file = null;
      FileOutputStream fileOutputStream = null;

      try
      {
         file = new File(pathname);
         fileOutputStream = new FileOutputStream(file);
      }
      catch (Exception e)
      {
         System.err.println("Error!! - Invalid output file path: "
               + pathname);
      }

      PDFGraphics pdfGraphics = null;
      printJob = new PDFJob(fileOutputStream);

      if (info != null)
         printJob.getPDFDocument().setPDFInfo(info);

      pageCount = pageable.getNumberOfPages();
      for (int pageIndex = 0; pageIndex < pageCount; pageIndex++)
      {
         pageFormat = pageable.getPageFormat(pageIndex);

         pdfGraphics = (PDFGraphics) printJob.getGraphics(pageFormat);
         printable = pageable.getPrintable(pageIndex);
         printable.print(pdfGraphics, pageFormat, pageIndex);
         pdfGraphics.dispose();
      }

      printJob.end();

   }

   @Override
   public boolean printDialog() throws HeadlessException
   {
      return true;
   }

   /**
    * Sets the author for this document.
    * 
    * @param author
    *            the author's name.
    */
   public void setAuthor(String author)
   {
      info.setAuthor(author);
   }

   @Override
   public void setCopies(int copies)
   {
      // Will be ignored, but add attribute anyway
      attributes.add(new Copies(copies));
   }

   /**
    * Sets the creator for this document.
    * 
    * @param creator
    *            the application name.
    */
   public void setCreator(String creator)
   {
      info.setCreator(creator);
   }

   @Override
   public void setJobName(String jobName)
   {
      attributes.add(new JobName(jobName, Locale.getDefault()));

      if (info.getTitle() == null)
         info.setTitle(jobName);
   }

   @Override
   public void setPageable(Pageable document) throws NullPointerException
   {
      if (document == null)
         throw new NullPointerException("Pageable cannot be null.");
      this.pageable = document;
   }

   @Override
   public void setPrintable(Printable painter)
   {
      this.printable = painter;
   }

   @Override
   public void setPrintable(Printable painter, PageFormat format)
   {
      this.printable = painter;
      this.pageFormat = format;
   }

   /**
    * Sets the title for this document.
    * 
    * @param title
    *            the document title.
    */
   public void setTitle(String title)
   {
      info.setTitle(title);
   }

   @Override
   public PageFormat validatePage(PageFormat page)
   {
      return (PageFormat) page.clone();
   }

}
